首先看到这到题,很容易想到AC自动机,但这个题它求的是方案,并且没有告诉我们母串,所以这个时候我就们可以尝试用搜索枚举每一种方案

部分代码

void dfs(int now, int j, bool flag)//now代表枚举了的文章长度,j代表AC自动机的下标,flag代表当前的文章是否合法
{
if (now == m)
{
ans = (ans + flag) % MOD;
return;
}
for (int i = 0; i < 26; i++)
{
int t = tr[j][i];
bool is = true;
while (t)
{
if (cnt[t])
{
dfs(now + 1, tr[j][i], true);
is = false;
break;
}
t = net[t];
}
if (is)
dfs(now + 1, tr[j][i], flag);
}
}

这个暴搜只能的20分,于是尝试优化,改成了记忆化搜索,用一个数组将之前搜过的状态存起来

int dfs(int now, int j, bool flag)
{
if (rec[now][j][flag])
return rec[now][j][flag];
if (now == m)
return flag;
int res = 0;
for (int i = 0; i < 26; i++)
{
int t = tr[j][i];
bool is = true;
while (t)
{
if (cnt[t])
{
res = (res + dfs(now + 1, tr[j][i], true)) % MOD;
is = false;
break;
}
t = net[t];
}
if (is)
res = (res + dfs(now + 1, tr[j][i], flag)) % MOD;
}
rec [now][j][flag] = res;
return res;
}

但了记忆化之后任然只能得70分,由于我tcl,已经找不出其他地方优化了,所以就只能考虑DP了,有了记忆化搜索的经验,那么DP就相对好想了,定于一个三维数组fi,j,kf_{i,j,k},含义同上面的搜索,i表示当前的文章长度,j表示AC自动机的位置,k表示文章是否合法,具体的转移方程放在代码里解释

完整代码

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int N = 1e5 + 5, MOD = 1e4 + 7;

char str[N];
int tr[N][26], net[N], cnt[N], idx, ans, n, m;
int f[105][N][2];
queue<int> q;

void insert()
{
int p = 0;
for (int i = 0; str[i]; i++)
{
int t = str[i] - 'A';
if (!tr[p][t])
tr[p][t] = ++idx;
p = tr[p][t];
}
cnt[p]++;
}

void build()
{
for (int i = 0; i < 26; i++)
if (tr[0][i])
q.push(tr[0][i]);
while (!q.empty())
{
int t = q.front();
q.pop();
for (int i = 0; i < 26; i++)
{
int p = tr[t][i];
if (!p)
tr[t][i] = tr[net[t]][i];
else
{
net[p] = tr[net[t]][i];
q.push(p);
}
}
}
}

int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%s", &str);
insert();
}
build();//AC自动机板子,就不解释了
f[0][0][0] = 1;//初始状态
for (int i = 0; i < m; i++)
{
for (int j = 0; j <= idx; j++)
{
for (int k = 0; k < 26; k++)
{
bool flag = false;
int p = tr[j][k];
while (p)
{
if (cnt[p])
{
flag = true;
break;
}
p = net[p];
}//判断下一个位置是否存在一个字典中的单词
if (flag)
f[i + 1][tr[j][k]][1] = (f[i + 1][tr[j][k]][1] + f[i][j][0] + f[i][j][1]) % MOD;//如果存在,就把当前位置的所有方案加上
else //不存在
{
f[i + 1][tr[j][k]][0] = (f[i + 1][tr[j][k]][0] + f[i][j][0]) % MOD;//加上不合法的方案
f[i + 1][tr[j][k]][1] = (f[i + 1][tr[j][k]][1] + f[i][j][1]) % MOD;//加上合法的方案
}
}
}
}
int ans = 0;
for (int i = 0; i <= idx; i++)
ans = (ans + f[m][i][1]) % MOD;//将所有方案加起来
printf("%d", ans);
return 0;
}